package org.myspringframework.aop;


import org.aopalliance.intercept.MethodInterceptor;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

//AOP代理配置管理器的基类。
//包装切面通知信息,用于把代理、拦截、匹配的各项属性包装到一个类中，方便在 Proxy 实现类进行使用。
public class AdvisedSupport {

    //是否直接对目标类进行代理，而不是通过接口产生代理,为true使用cglib代理
    private boolean proxyTargetClass = false;
    // 被代理的目标对象
    private TargetSource targetSource;
    //存放匹配的advisor低级切面
    //todo:排序
    private List<Advisor> advisors;
    //每个方法对应的methodInterceptor通知链缓存的缓存
    private  Map<MethodCacheKey, List<Object>> methodCache=new ConcurrentHashMap<>();
    //通知链工厂，用于生成方法对应的通知链
    AdvisorChainFactory advisorChainFactory=new DefaultAdvisorChainFactory();



    //标记是否直接对目标类进行代理，而不是通过接口产生代理
    public boolean isProxyTargetClass() {
        return proxyTargetClass;
    }

    public void setProxyTargetClass(boolean proxyTargetClass) {
        this.proxyTargetClass = proxyTargetClass;
    }

    //获取方法对应的methodInterceptor
    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) throws Exception {
        //先从缓存中取
        MethodCacheKey cacheKey = new MethodCacheKey(method);
        List<Object> cached = (List)this.methodCache.get(cacheKey);

        if (cached == null) {
            //缓存中取不到，使用通知链工厂获取
            cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this, method, targetClass);
            //放入缓存中
            this.methodCache.put(cacheKey, cached);
        }

        return cached;
    }

    public TargetSource getTargetSource() {
        return targetSource;
    }

    public void setTargetSource(TargetSource targetSource) {
        this.targetSource = targetSource;
    }

    public List<Advisor> getAdvisors() {
        return advisors;
    }

    public void setAdvisors(List<Advisor> advisors) {
        this.advisors = advisors;
    }

    //方法缓存key类，用于缓存methodInterceptor链
    private static final class MethodCacheKey {
        private final Method method;
        private final int hashCode;

        public MethodCacheKey(Method method) {
            this.method = method;
            this.hashCode = method.hashCode();
        }

        public boolean equals(Object other) {
            return this == other || other instanceof MethodCacheKey && this.method == ((MethodCacheKey)other).method;
        }

        public int hashCode() {
            return this.hashCode;
        }

        public String toString() {
            return this.method.toString();
        }

    }
}